package org.lex.swt;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.eclipse.swt.widgets.Display;

public abstract class SWTWorker<T> implements RunnableFuture<T> {
	/**
	 * number of worker threads.
	 */
	private static final int MAX_WORKER_THREADS = 10;

	/**
	 * everything is run inside this FutureTask. Also it is used as a delegatee
	 * for the Future API.
	 */
	private final FutureTask<T> workFuture;

	/**
	 * Constructs this {@code SwingWorker}.
	 */
	public SWTWorker() {
		Callable<T> callable = new Callable<T>() {
			public T call() throws Exception {
				return doInBackground();
			}
		};

		workFuture = new FutureTask<T>(callable) {
			@Override
			protected void done() {
				doneUIT();
			}
		};
	}

	private void doneUIT() {
		try {
			Display dis = Display.getCurrent();
			if (null == dis) {
				dis = Display.getDefault();
			}
			dis.asyncExec(new Runnable() {
				public void run() {
					done();
				}
			});
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Computes a result, or throws an exception if unable to do so.
	 * 
	 * <p>
	 * Note that this method is executed only once.
	 * 
	 * <p>
	 * Note: this method is executed in a background thread.
	 * 
	 * 
	 * @return the computed result
	 * @throws Exception
	 *             if unable to compute a result
	 * 
	 */
	protected abstract T doInBackground() throws Exception;

	/**
	 * Sets this {@code Future} to the result of computation unless it has been
	 * cancelled.
	 */
	public final void run() {
		workFuture.run();
	}

	/**
	 * Executed on the <i>Event Dispatch Thread</i> after the {@code
	 * doInBackground} method is finished. The default implementation does
	 * nothing. Subclasses may override this method to perform completion
	 * actions on the <i>Event Dispatch Thread</i>. Note that you can query
	 * status inside the implementation of this method to determine the result
	 * of this task or whether this task has been cancelled.
	 * 
	 * @see #doInBackground
	 * @see #isCancelled()
	 * @see #get
	 */
	protected void done() {
	}

	public final void execute() {
		getWorkersExecutorService().execute(this);
	}

	// Future methods START
	/**
	 * {@inheritDoc}
	 */
	public final boolean cancel(boolean mayInterruptIfRunning) {
		return workFuture.cancel(mayInterruptIfRunning);
	}

	/**
	 * {@inheritDoc}
	 */
	public final boolean isCancelled() {
		return workFuture.isCancelled();
	}

	/**
	 * {@inheritDoc}
	 */
	public final boolean isDone() {
		return workFuture.isDone();
	}

	public final T get() throws InterruptedException, ExecutionException {
		return workFuture.get();
	}

	/**
	 * {@inheritDoc}
	 * <p>
	 * Please refer to {@link #get} for more details.
	 */
	public final T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
		return workFuture.get(timeout, unit);
	}

	// Future methods END

	private static ExecutorService executor = null;

	private static synchronized ExecutorService getWorkersExecutorService() {
		if (null == executor) {
			// this creates non-daemon threads.
			ThreadFactory threadFactory = new ThreadFactory() {
				final ThreadFactory defaultFactory = Executors.defaultThreadFactory();

				public Thread newThread(final Runnable r) {
					Thread thread = defaultFactory.newThread(r);
					thread.setName("SWTWorker-" + thread.getName());
					return thread;
				}
			};

			/*
			 * We want a to have no more than MAX_WORKER_THREADS running
			 * threads.
			 * 
			 * We want a worker thread to wait no longer than 1 second for new
			 * tasks before terminating.
			 */
			executor = new ThreadPoolExecutor(0, MAX_WORKER_THREADS, 1L, TimeUnit.SECONDS,
					new LinkedBlockingQueue<Runnable>(), threadFactory);
		}
		return executor;
	}
}
